package org.openkoala.security.shiro.realm;

import java.util.Map;

import javax.inject.Inject;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.cas.CasAuthenticationException;
import org.apache.shiro.cas.CasRealm;
import org.apache.shiro.cas.CasToken;
import org.apache.shiro.subject.PrincipalCollection;
import org.jasig.cas.client.authentication.AttributePrincipal;
import org.jasig.cas.client.validation.Assertion;
import org.jasig.cas.client.validation.TicketValidationException;
import org.jasig.cas.client.validation.TicketValidator;
import org.openkoala.security.facade.SecurityAccessFacade;
import org.openkoala.security.facade.SecurityConfigFacade;
import org.openkoala.security.facade.command.CreateUserCommand;
import org.openkoala.security.facade.dto.UserDTO;
import org.openkoala.security.shiro.CurrentUser;
import org.openkoala.security.shiro.RoleHandle;
import org.openkoala.security.shiro.extend.ShiroFilterChainManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class SecurityIntegrateCasRealm extends CasRealm  implements RoleHandle {

	private static final Logger LOGGER = LoggerFactory.getLogger(SecurityIntegrateCasRealm.class);
	
	@Inject
	private SecurityAccessFacade securityAccessFacade;
	
	@Inject
	private SecurityConfigFacade securityConfigFacade;
	
	@Inject
	private ShiroFilterChainManager shiroFilterChainManager;
	
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		CasToken casToken = (CasToken) token;
        if (token == null) {
            return null;
        }
        
        String ticket = (String)casToken.getCredentials();
        if (!org.apache.shiro.util.StringUtils.hasText(ticket)) {
            return null;
        }
        
        TicketValidator ticketValidator = ensureTicketValidator();

        try {
            // contact CAS server to validate service ticket
            Assertion casAssertion = ticketValidator.validate(ticket, getCasService());
            // get principal, user id and attributes
            AttributePrincipal casPrincipal = casAssertion.getPrincipal();
            String userId = casPrincipal.getName();
            LOGGER.debug("Validate ticket : {} in CAS server : {} to retrieve user : {}", new Object[]{
                    ticket, getCasServerUrlPrefix(), userId
            });

            Map<String, Object> attributes = casPrincipal.getAttributes();
            // refresh authentication token (user id + remember me)
            casToken.setUserId(userId);
            String rememberMeAttributeName = getRememberMeAttributeName();
            String rememberMeStringValue = (String)attributes.get(rememberMeAttributeName);
            boolean isRemembered = rememberMeStringValue != null && Boolean.parseBoolean(rememberMeStringValue);
            if (isRemembered) {
                casToken.setRememberMe(true);
            }
            
            ShiroUser shiroUser = null;
            UserDTO userDTO = securityAccessFacade.getUserByUserAccount(userId);
            if (userDTO == null) {
            	CreateUserCommand createUserCommand = new CreateUserCommand();
                createUserCommand.setUserAccount(userId);
                createUserCommand.setName(userId);
                securityConfigFacade.createUser(createUserCommand);
                shiroUser = new ShiroUser(userId, userId);
            } else {
            	shiroUser = new ShiroUser(userId, userDTO.getName());
            }
            SimpleAuthenticationInfo result = new SimpleAuthenticationInfo(shiroUser, ticket, getName());
            return result;
        } catch (TicketValidationException e) { 
            throw new CasAuthenticationException("Unable to validate ticket [" + ticket + "]", e);
        }
	}
	
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		ShiroUser shiroUser = (ShiroUser) principals.getPrimaryPrincipal();
		SimpleAuthorizationInfo result = new SimpleAuthorizationInfo();
		result.setRoles(shiroUser.getRoles());
		result.setStringPermissions(shiroUser.getPermissions());
		return result;
	}

	/**
	 * 切换角色
	 * 
	 * @param roleName
	 */
	public void switchOverRoleOfUser(String roleName) {
		PrincipalCollection principalCollection = CurrentUser.getPrincipals();
		ShiroUser shiroUser = (ShiroUser) principalCollection.getPrimaryPrincipal();
		shiroUser.setRoleName(roleName);
		this.doGetAuthorizationInfo(principalCollection);
	}

	public void resetRoleName(String name) {
		switchOverRoleOfUser(name);
		shiroFilterChainManager.initFilterChain();
	}
	
}
